const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

// 利用x的值来判断是调用promise2的resolve还是reject
function resolvePromise(promise2, x, resolve, reject) {
    // 1. x的值是promise2
    if (promise2 === x) {
        return reject(new TypeError('类型错误'));
    }
    // 2. 兼容别人的promise，核心就是判断如果是promise就调then，不是promise就直接返回
    // 1) 对象或者函数，有可能是promise
    if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try { // 有可能then方法是通过defineProperty来实现的，取值是可能会发生异常
            let then = x.then;
            if (typeof then === 'function') { // 认为是promise了
                then.call(x, y => {  // 为什么不直接x.then这么写呢？因为这样写会触发getter，可能会发生异常
                    resolvePromise(promise2, y, resolve, reject); // 嵌套promise的情况：resolve的值还是promise
                }, r => {
                    reject(r);
                });
            } else {
                resolve(x);
            }
        } catch(e) {
            reject(e);
        }
    // 2）肯定不是promise，是普通值。直接放到promise2.resolve中
    } else {
        resolve(x);
    }
}

class Promise {
    constructor(executor) {
        this.status = PENDING;
        this.value = undefined;  // 成功的数据
        this.reason = undefined; // 失败的数据
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];

        const resolve = (value) => {
            // 直接resolve一个promise
            if (value instanceof Promise) {
                return value.then(resolve, reject);
            }

            if (this.status === PENDING) {
                this.status = FULFILLED;
                this.value = value;
                // 发布
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }

        const reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED;
                this.reason = reason;
                // 发布
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onFulfilled, onRejected) {
        // 穿透问题
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
        onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
        // then调用后，返回一个新promise
        let promise2 = new Promise((resolve, reject) => {
            // 直接成功或失败
            if (this.status === FULFILLED) {
                setTimeout(() => { // 加定时器，异步的目的是拿到promise2
                    try {
                        let x = onFulfilled(this.value);
                        // x有可能是promise，如果是promise需要看下这个promise是成功还是失败，成功则把成功的结果调用promise2的resolve传递进去。
                        // 总结：x的值决定是调用promise2的resolve还是reject，如果是promise则取他的状态，如果是普通值，则直接调用resolve；
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            }
            if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                }, 0);
            }
            // 有定时器的情形，先保存起来，resolve的时候依次调用（订阅）
            if (this.status === PENDING) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    }, 0);
                });
            }
        })
        return promise2;
    }
    catch(errorFn) {
        return this.then(null, errorFn);
    }

    static resolve(value) {
        return new Promise((resolve, reject) => {
            resolve(value);
        })
    }
    static reject(value) {
        return new Promise((resolve, reject) => {
            reject(value);
        })
    }
}

module.exports = Promise;